grammar EScript;

options {
    language=Java;
}

@parser::members {
    private boolean lineTerminatorAhead() {
        int possibleIndexEosToken = this.getCurrentToken().getTokenIndex() - 1;
        Token ahead = _input.get(possibleIndexEosToken);
        if (ahead.getChannel() != Lexer.HIDDEN) {
            return false;
        }
        if (ahead.getType() == LineTerminator) {
            return true;
        }
        return ahead.getType() == EScriptLexer.WhiteSpace;
    }

    protected boolean closeBrace() {
        return _input.LT(1).getType() == SemiColon;
    }
}



MultiLineComment:               '/*' .*? '*/'                   -> channel(HIDDEN);
SingleLineComment:              '//' ~[\r\n\u2028\u2029]*       -> channel(HIDDEN);
WhiteSpace:                     [\t\u000B\u000C\u0020\u00A0]+   -> channel(HIDDEN);
LineTerminator:                 [\r\n\u2028\u2029]              -> channel(HIDDEN);

Identifier: [_a-zA-Z][_a-zA-Z0-9]*;

HexIntegerLiteral: '0' [xX] [0-9a-fA-F]+;
DecimalLiteral: DIGIT+;
OctalIntegerLiteral: '0' [oO] [0-7]+;
BinaryIntegerLiteral: '0' [bB] [01]+;
FloatLiteral: DIGIT+ '.' DIGIT+;
StringLiteral
    : '"' (~[\r\n\u2028\u2029]*?) '"'
    | '\'' (~[\r\n\u2028\u2029]*?) '\''
    | '`' (.*?) '`'
    ;

// characters
OpenBracket: '[';
CloseBracket: ']';
OpenParen: '(';
CloseParen: ')';
OpenBrace: '{';
CloseBrace: '}';
Comma: ',';
SemiColon: ';';
Colon: ':';
Assign: '=';
Dot: '.';
Plus: '+';
Minus: '-';
BitNot: '~';
BitAnd: '&';
BitOr: '|';
BitXor: '^';
Not: '!';
Multiply: '*';
Divide: '/';
Modulus: '%';
LogicAnd: '&&';
LogicOr: '||';
LessThan: '<';
LessThanEquals: '<=';
GreaterThan: '>';
GreaterThanEquals: '>=';
Equals: '==';
NotEquals: '!=';

fragment DIGIT: [0-9];

chunk
    : statementList? EOF
    ;

statementList
    : statement+
    ;

statement
    : assignStatement
    | returnStatement
    | callFunctionStatement
    | ifStatement
    | forStatement
    | breakStatement
    | continueStatement
    | blockStatement
    | functionDeclarationStatement
    ;

blockStatement: '{' statementList? '}';

expression
    : literal                                                   #LiteralExpression
    | Identifier                                                #IdentifierExpression
    | 'func' '(' parameters? ')' blockStatement                 #AnonymousFunctionExpression
    | '[' expressionList? ']'                                   #ListExpression
    | '{' dictItems? '}'                                        #DictExpression
    | expression Dot Identifier                                 #MemberExpression
    | '(' expression ')'                                        #ParenthesizedExpression
    | expression '[' expression ']'                             #ListIndexExpression
    | expression '(' expressionList? ')'                        #CallFunctionExpression
    | expression op=(Multiply | Divide | Modulus) expression    #MultiplicativeExpression
    | expression op=(Plus | Minus) expression                   #AdditiveExpression
    | expression op=(LessThan | LessThanEquals | GreaterThan | GreaterThanEquals) expression
                                                                #RelationalExpression
    | expression op=(Equals | NotEquals) expression                #EqualityExpression
    | expression BitAnd     expression                          #BitAndExpression
    | expression BitXor     expression                          #BitXorExpression
    | expression BitOr      expression                          #BitOrExpression
    | expression LogicAnd   expression                          #LogicAndExpression
    | expression LogicOr    expression                          #LogicOrExpression
    ;

integerLiteral
    : HexIntegerLiteral
    | DecimalLiteral
    | OctalIntegerLiteral
    | BinaryIntegerLiteral
    ;

numberLiteral
    : integerLiteral
    | FloatLiteral
    ;

literal
    : op=(Plus | Minus)? right=numberLiteral
    | StringLiteral
    ;

expressionList
    : expression (',' expression)*
    ;

dictItems
    : expression ':' expression (',' expression ':' expression)*
    ;

assignable
    : Identifier                        #IdentifierAssignable
    | expression '[' expression ']'     #ListIndexAssignable
    | expression '.' Identifier         #MemberAttributeAssignable
    ;

assignStatement
    : assignable (',' assignable)* '=' expression (',' expression)* eos__
    ;

callFunctionStatement
    : expression '(' expressionList? ')' eos__
    ;

returnStatement
    : 'return' expression? eos__
    ;

parameters
    : Identifier (',' Identifier)*
    ;

functionDeclarationStatement
    : 'func' Identifier '(' parameters? ')' blockStatement
    ;

ifStatement
    : 'if' expression blockStatement ('else' 'if' expression blockStatement)* ('else' else_=blockStatement)?
    ;

forStatement
    : 'for' expression? blockStatement eos__
    | 'for' init=assignStatement? ';' expression? ';' inc=assignStatement? blockStatement eos__
    ;


continueStatement: 'continue' ;

breakStatement: 'break' ;

eos__
    : SemiColon
    | EOF
    | {this.lineTerminatorAhead()}?
    | {this.closeBrace()}?
    ;
